package com.jinke.api.modules.app.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import com.jinke.api.common.api.CommonResult;
import com.jinke.api.common.domain.AmountDescription;
import com.jinke.api.common.domain.RechargeInvoicePo;
import com.jinke.api.common.exception.Asserts;
import com.jinke.api.common.util.FedexRateTool;
import com.jinke.api.common.util.Global;
import com.jinke.api.common.util.PdfUtil;
import com.jinke.api.modules.api.conf.ApiUrlsConfig;
import com.jinke.api.modules.app.controller.param.MaterialRechargeParam;
import com.jinke.api.modules.app.enums.*;
import com.jinke.api.modules.app.mapper.UmsSubscribeLogMapper;
import com.jinke.api.modules.app.mapper.UserRechargeMapper;
import com.jinke.api.modules.app.model.BalanceLog;
import com.jinke.api.modules.app.model.UmsSubscribeLog;
import com.jinke.api.modules.app.model.UserRecharge;
import com.jinke.api.modules.app.request.UserRechargeRequest;
import com.jinke.api.modules.app.response.UserRechargeListVO;
import com.jinke.api.modules.app.service.BalanceLogService;
import com.jinke.api.modules.app.service.UserRechargeService;
import com.jinke.api.modules.app.util.MathUtil;
import com.jinke.api.modules.base.service.BaseShipService;
import com.jinke.api.modules.base.service.ShipFactory;
import com.jinke.api.modules.third.easypost.model.request.CancelInsuranceDTO;
import com.jinke.api.modules.ums.constant.ConfigConstant;
import com.jinke.api.modules.ums.model.Channel;
import com.jinke.api.modules.ums.model.SystemConfig;
import com.jinke.api.modules.ums.model.UmsAdmin;
import com.jinke.api.modules.ums.request.ApproveRequest;
import com.jinke.api.modules.ums.request.FindZoneRequest;
import com.jinke.api.modules.ums.request.RechargeRequest;
import com.jinke.api.modules.ums.service.SystemConfigService;
import com.jinke.api.modules.ums.service.UmsAdminService;
import com.jinke.api.modules.ums.vo.FindZoneVo;
import com.jinke.api.modules.ums.vo.SimpleUser;
import com.jinke.api.security.util.AdminUserUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.apache.velocity.VelocityContext;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author macro
 * @since 2024-01-13
 */
@Service
@Slf4j
public class UserRechargeServiceImpl extends ServiceImpl<UserRechargeMapper, UserRecharge> implements UserRechargeService {

    @Resource
    UmsAdminService umsAdminService;
    @Resource
    private ShipFactory shipFactory;
    @Resource
    UserRechargeMapper userRechargeMapper;

    @Resource
    BalanceLogService balanceLogService;

    @Resource
    private PlatformTransactionManager transactionManager;

    @Resource
    private SystemConfigService systemConfigService;

    @Resource
    private ApiUrlsConfig apiUrlsConfig;

    @Resource
    private UmsSubscribeLogMapper subscribeLogMapper;

    @Resource
    private FedexRateTool fedexRateTool;

    @Override
    public Page<UserRechargeListVO> list(UserRechargeRequest request) {
        List<Integer> searchIds = new ArrayList<>();
        String email = request.getEmail();
        if (StringUtils.isNotEmpty(email)) {
            LambdaQueryWrapper<UmsAdmin> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.likeRight(UmsAdmin::getEmail, email);
            searchIds = umsAdminService.list(queryWrapper).stream().map(UmsAdmin::getId).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(searchIds)) {
                searchIds = Arrays.asList(-1);
            }
        }

        Page<UserRecharge> page = new Page<>(request.getPageNum(), request.getPageSize());
        QueryWrapper<UserRecharge> wrapper = new QueryWrapper<>();
        List<String> ids = request.getIds();
        wrapper.lambda()
                .eq(ObjectUtil.isNotNull(request.getId()), UserRecharge::getId, request.getId())
                .in(!searchIds.isEmpty(), UserRecharge::getUserId, searchIds)
                .in(CollectionUtils.isNotEmpty(ids), UserRecharge::getId, ids)
                .eq(request.getStatus() != null, UserRecharge::getStatus, request.getStatus())
                .eq(request.getPaymentType() != null, UserRecharge::getPaymentType, request.getPaymentType())
                .ge(request.getStartDate() != null, UserRecharge::getCreatedAt, request.getStartDate())
                .le(request.getEndDate() != null, UserRecharge::getCreatedAt, request.getEndDate())
                .orderByDesc(UserRecharge::getId);


        UmsAdmin userInfo = AdminUserUtil.getUserInfo();
        if (userInfo.isSeller()) { // 如果是销售只能看自己或者自己相关的数据
            Integer sellerId = userInfo.getId();
            wrapper.lambda().and(wp -> {
                wp.eq(UserRecharge::getUserId, sellerId)
                        .or().eq(UserRecharge::getSellerId, sellerId);
            });
        } else {
            wrapper.lambda().eq(request.getUserId() != null, UserRecharge::getUserId, request.getUserId());
        }

        Page<UserRecharge> pageResult = page(page, wrapper);
        // 获取列表中的用户ID
        List<Integer> userIds = pageResult.getRecords().stream().map(UserRecharge::getUserId).distinct().collect(Collectors.toList());
        // 关联用户数据
        Map<Integer, SimpleUser> usersMap = umsAdminService.getMaps(userIds);
        //取出获取所有记录的审核员ID，去重，将其存储到一个列表中
        List<Integer> auditorIds = pageResult.getRecords().stream().map(UserRecharge::getAdminId).distinct().collect(Collectors.toList());
        //根据审核员ID列表 auditorIds，调用 umsAdminService 的 getMaps 方法来获取审核员信息
        Map<Integer, SimpleUser> auditorMap = umsAdminService.getMaps(auditorIds);

        //复制分页2
        Page<UserRechargeListVO> pageResult2 = new Page<>();
        BeanUtils.copyProperties(pageResult, pageResult2);

        List<UserRechargeListVO> userRechargeListVOS = pageResult.getRecords().stream().map(x -> {
            //创建新的视图对象
            UserRechargeListVO userRechargeListVO = new UserRechargeListVO();
            //将x 复制到新的视图对象里面
            BeanUtils.copyProperties(x, userRechargeListVO);
            //将获取到的用户 ID信息set到Vo里面
            userRechargeListVO.setUser(usersMap.getOrDefault(x.getUserId(), null));
            //通过判断请求中的用户ID是否为空，来确定是否设置审核员信息
            if (request.getUserId() == null) {
                userRechargeListVO.setAuditor(auditorMap.getOrDefault(x.getAdminId(), null));
            }
            return userRechargeListVO;
        }).collect(Collectors.toList());//使用collect方法将得到的接口收集到列表中

        pageResult2.setRecords(userRechargeListVOS);
        return pageResult2;
    }

    @Override
    public boolean approve(ApproveRequest request) {
        LambdaQueryWrapper<UserRecharge> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserRecharge::getId, request.getId());
        queryWrapper.eq(UserRecharge::getStatus, UserRechargeStatus.PENDING);
        UserRecharge userRecharge = getOne(queryWrapper);
        if (userRecharge == null) {
            Asserts.fail("未找到需要审核的记录");
        }
        if (request.getUsdAmount() != null && request.getUsdAmount().compareTo(BigDecimal.ZERO) > 0) {
            userRecharge.setUsdAmount(request.getUsdAmount());
        }
        // 判断审核状态
        if (request.getStatus().equals(UserRechargeStatus.SUCCESS)) {
            //同意
            return passedRechargeForcaiwu(userRecharge, request);
        } else if (request.getStatus().equals(UserRechargeStatus.REJECTED)) {
            return rejectRecharge(userRecharge, request);
        } else {
            Asserts.fail("参数错误");
        }
        return false;
    }

    /**
     * 充值审核通过
     * @param userRecharge
     * @param request
     * @return
     */
    public boolean passedRecharge(UserRecharge userRecharge, ApproveRequest request) {
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
        try {
            LambdaQueryWrapper<UmsAdmin> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(UmsAdmin::getId, userRecharge.getUserId());
            queryWrapper.last("FOR UPDATE");
            UmsAdmin user = umsAdminService.getOne(queryWrapper);
            if (user == null) {
                Asserts.fail("未找到用户");
            }
            BigDecimal usdAmount = userRecharge.getUsdAmount();
            BalanceType balanceType = userRecharge.getBalanceType();
            BigDecimal balance = user.getBalance();
            BigDecimal afterBalance = balance.add(usdAmount);
            // 增加余额
            UpdateWrapper<UmsAdmin> updateWrapper = new UpdateWrapper<>();
            updateWrapper.eq("id", user.getId());
            if (BalanceType.BALANCE.equals(balanceType)) {
                updateWrapper.setSql("balance=balance+" + usdAmount);
                //信用额度清零
                updateWrapper.setSql("credit_balance = 0");
            } else if (BalanceType.EARNEST.equals(balanceType)) {
                afterBalance = user.getEarnestBalance().add(usdAmount);
                updateWrapper.setSql("earnest_balance=earnest_balance+" + usdAmount);
            } else if (BalanceType.DEPOSIT.equals(balanceType)) {
                afterBalance = user.getDepositBalance().add(usdAmount);
                updateWrapper.setSql("deposit_balance=deposit_balance+" + usdAmount);
            } else if (BalanceType.CREDIT.equals(balanceType)) {
                afterBalance = user.getCreditBalance().add(usdAmount);
                updateWrapper.setSql("credit_balance=credit_balance+" + usdAmount);
            }
            if (!umsAdminService.update(null, updateWrapper)) {
                Asserts.fail("更新用户余额失败");
            }

            // 增加余额变动明细
            BalanceLog log = new BalanceLog();
            log.setUserId(userRecharge.getUserId());
            log.setAfterValue(afterBalance);
            log.setChangeValue(usdAmount);
            String remark = request.getAuditRemark();
            if (StringUtils.isBlank(remark)) {
                remark = "后台充值";
            }
            log.setRemark(remark);
            log.setType(BalanceLogType.RECHARGE);
            log.setSellerId(user.getSellerId());

            log.setBalanceType(balanceType);
            if (!balanceLogService.save(log)) {
                Asserts.fail("写入充值明细失败");
            }

            // 变更记录状态
            LambdaUpdateWrapper<UserRecharge> rechargeUpdateWrapper = new LambdaUpdateWrapper<>();
            rechargeUpdateWrapper.eq(UserRecharge::getId, userRecharge.getId());
            rechargeUpdateWrapper.set(UserRecharge::getStatus, request.getStatus());
            rechargeUpdateWrapper.set(UserRecharge::getAuditRemark, request.getAuditRemark());
            rechargeUpdateWrapper.set(UserRecharge::getAuditTime, LocalDateTime.now());
            rechargeUpdateWrapper.set(request.getUsdAmount() != null && request.getUsdAmount().compareTo(BigDecimal.ZERO) > 0, UserRecharge::getUsdAmount, usdAmount);
            rechargeUpdateWrapper.set(request.getPaymentType() != null, UserRecharge::getPaymentType, request.getPaymentType());
            rechargeUpdateWrapper.set(UserRecharge::getAdminId, AdminUserUtil.getUserId());
            rechargeUpdateWrapper.eq(UserRecharge::getStatus, UserRechargeStatus.PENDING);
            int result = userRechargeMapper.update(null, rechargeUpdateWrapper);
            if (result <= 0) {
                Asserts.fail("更新充值记录状态失败");
            }

            // 如果上述操作都成功，那么提交事务
            transactionManager.commit(status);
        } catch (Exception ex) {
            // 如果出现异常，那么回滚事务
            transactionManager.rollback(status);
            throw ex;
        }

        return true;
    }
    public boolean passedRechargeForcaiwu(UserRecharge userRecharge, ApproveRequest request) {
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
        try {
            LambdaQueryWrapper<UmsAdmin> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(UmsAdmin::getId, userRecharge.getUserId());
            queryWrapper.last("FOR UPDATE");
            UmsAdmin user = umsAdminService.getOne(queryWrapper);
            if (user == null) {
                Asserts.fail("未找到用户");
            }
            BigDecimal usdAmount = userRecharge.getUsdAmount();
            BalanceType balanceType = request.getBalanceType();
            BigDecimal balance = user.getBalance();
            BigDecimal afterBalance = balance.add(usdAmount);
            // 增加余额
            UpdateWrapper<UmsAdmin> updateWrapper = new UpdateWrapper<>();
            updateWrapper.eq("id", user.getId());
            if (BalanceType.BALANCE.equals(balanceType)) {
                updateWrapper.setSql("balance=balance+" + usdAmount);
                //信用额度清零
                updateWrapper.setSql("credit_balance = 0");
            } else if (BalanceType.EARNEST.equals(balanceType)) {
                afterBalance = user.getEarnestBalance().add(usdAmount);
                updateWrapper.setSql("earnest_balance=earnest_balance+" + usdAmount);
            } else if (BalanceType.DEPOSIT.equals(balanceType)) {
                afterBalance = user.getDepositBalance().add(usdAmount);
                updateWrapper.setSql("deposit_balance=deposit_balance+" + usdAmount);
            } else if (BalanceType.CREDIT.equals(balanceType)) {
                afterBalance = user.getCreditBalance().add(usdAmount);
                updateWrapper.setSql("credit_balance=credit_balance+" + usdAmount);
            }
            if (!umsAdminService.update(null, updateWrapper)) {
                Asserts.fail("更新用户余额失败");
            }

            // 增加余额变动明细
            BalanceLog log = new BalanceLog();
            log.setUserId(userRecharge.getUserId());
            log.setAfterValue(afterBalance);
            log.setChangeValue(usdAmount);
            String remark = request.getAuditRemark();
            if (StringUtils.isBlank(remark)) {
                remark = "后台充值";
            }
            log.setRemark(remark);
            log.setType(BalanceLogType.RECHARGE);
            log.setSellerId(user.getSellerId());

            log.setBalanceType(balanceType);
            if (!balanceLogService.save(log)) {
                Asserts.fail("写入充值明细失败");
            }

            // 变更记录状态
            LambdaUpdateWrapper<UserRecharge> rechargeUpdateWrapper = new LambdaUpdateWrapper<>();
            rechargeUpdateWrapper.eq(UserRecharge::getId, userRecharge.getId());
            rechargeUpdateWrapper.set(UserRecharge::getStatus, request.getStatus());
            rechargeUpdateWrapper.set(UserRecharge::getAuditRemark, request.getAuditRemark());
            rechargeUpdateWrapper.set(UserRecharge::getAuditTime, LocalDateTime.now());
            rechargeUpdateWrapper.set(request.getUsdAmount() != null && request.getUsdAmount().compareTo(BigDecimal.ZERO) > 0, UserRecharge::getUsdAmount, usdAmount);
            rechargeUpdateWrapper.set(request.getPaymentType() != null, UserRecharge::getPaymentType, request.getPaymentType());
            rechargeUpdateWrapper.set(UserRecharge::getAdminId, AdminUserUtil.getUserId());
            rechargeUpdateWrapper.set(UserRecharge::getBalanceType, balanceType);
            rechargeUpdateWrapper.eq(UserRecharge::getStatus, UserRechargeStatus.PENDING);
            int result = userRechargeMapper.update(null, rechargeUpdateWrapper);
            if (result <= 0) {
                Asserts.fail("更新充值记录状态失败");
            }

            // 如果上述操作都成功，那么提交事务
            transactionManager.commit(status);
        } catch (Exception ex) {
            // 如果出现异常，那么回滚事务
            transactionManager.rollback(status);
            throw ex;
        }

        return true;
    }

    @Transactional
    public boolean rejectRecharge(UserRecharge userRecharge, ApproveRequest request) {
        // 变更审核记录状态即可
        LambdaUpdateWrapper<UserRecharge> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(UserRecharge::getId, userRecharge.getId());
        updateWrapper.set(UserRecharge::getStatus, request.getStatus());
        updateWrapper.set(UserRecharge::getAuditRemark, request.getAuditRemark());
        updateWrapper.set(UserRecharge::getAuditTime, LocalDateTime.now());
        updateWrapper.set(UserRecharge::getAdminId, AdminUserUtil.getUserId());
        updateWrapper.eq(UserRecharge::getStatus, UserRechargeStatus.PENDING);
        return update(null, updateWrapper);
    }

    @Override
    @Transactional
    public boolean recharge(RechargeRequest request) {
        UmsAdmin user = umsAdminService.getAdminByKeyword(request.getUsername());

        UserRecharge userRecharge = new UserRecharge();
        userRecharge.setUserId(user.getId());
        userRecharge.setCoinType(CoinType.USDT);
        userRecharge.setAdminId(AdminUserUtil.getUserId());
        userRecharge.setBalanceType(request.getBalanceType());
        userRecharge.setCredentialUrl(request.getCredentialUrl());
        Integer addType = request.getAddType();

        BigDecimal amount = addType == 0 ? request.getAmount() : request.getAmount().multiply(BigDecimal.valueOf(-1));
        userRecharge.setAmount(amount);

        if (CoinType.USDT.equals(userRecharge.getCoinType())) {
            userRecharge.setUsdAmount(userRecharge.getAmount());
        } else {
            SystemConfig systemConfig = systemConfigService.getByKey(ConfigConstant.USD_RATE);
            BigDecimal exchangeRate = BigDecimal.ONE;
            if (systemConfig != null) {
                exchangeRate = systemConfig.toBigDecimal();
            }

            userRecharge.setUsdAmount(userRecharge.getAmount().divide(exchangeRate, RoundingMode.CEILING));
        }
        String remark = request.getRemark();
        if (StringUtils.isBlank(remark)) {
            remark = "后台充值";
        }
        userRecharge.setRemark(remark);

        save(userRecharge);

        ApproveRequest approveRequest = new ApproveRequest();
        approveRequest.setStatus(UserRechargeStatus.SUCCESS);
        approveRequest.setAuditRemark(remark);
        approveRequest.setId(userRecharge.getId());

        return passedRecharge(userRecharge, approveRequest);
    }

    /**
     * 退钱
     * @param userId
     * @param amount
     * @param type
     * @param remark
     * @param orderNo
     * @param details
     * @return
     */
    @Override
    public boolean incr(Integer userId, BigDecimal amount, BalanceLogType type, String remark, String orderNo, List<BalanceLog.Detail> details) {
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
        try {
            LambdaQueryWrapper<UmsAdmin> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(UmsAdmin::getId, userId);
            queryWrapper.last("FOR UPDATE");
            UmsAdmin user = umsAdminService.getOne(queryWrapper);
            if (user == null) {
                Asserts.fail("未找到用户");
            }

            BigDecimal balance = user.getBalance();
            BigDecimal afterBalance = balance.add(amount);

            // 如果退钱 不管余额是否充足
            if (BigDecimal.ZERO.compareTo(afterBalance) > 0 && BigDecimal.ZERO.compareTo(amount) > 0) {
                Asserts.fail("Insufficient Balance");
            }

            // 增加余额变动明细
            BalanceLog log = new BalanceLog();
            log.setUserId(userId.intValue());
            log.setAfterValue(afterBalance);
            log.setChangeValue(amount);
            log.setRemark(remark);
            log.setType(type);
            log.setOrderNo(orderNo);
            log.setSellerId(user.getSellerId());



            Gson gson = new Gson();
//            Type mapType = new TypeToken<LinkedHashMap<String, String>>() {}.getType();
//
//            List<LinkedHashMap<String, String>> convertedDetails = details.stream()
//                    .map(detail -> gson.fromJson(gson.toJson(detail), mapType))
//                    .collect(Collectors.toList());

            log.setDetails(gson.toJson(details));
            if (!balanceLogService.save(log)) {
                Asserts.fail("写入明细失败");
            }

            // 变更余额
            UpdateWrapper<UmsAdmin> updateWrapper = new UpdateWrapper<>();
            updateWrapper.eq("id", user.getId());

            // 如果是扣钱，余额必须充足
            if (BigDecimal.ZERO.compareTo(amount) > 0) {
                updateWrapper.ge("balance", amount);
            }

            updateWrapper.setSql("balance=balance + " + amount);
            if (!umsAdminService.update(null, updateWrapper)) {
                Asserts.fail("更新用户余额失败");
            }


            // 如果上述操作都成功，那么提交事务
            transactionManager.commit(status);
        } catch (Exception ex) {
            // 如果出现异常，那么回滚事务
            transactionManager.rollback(status);
            throw ex;
        }

        return true;
    }



    @Override
    public boolean decrTransfer(Integer userId, BigDecimal amount, BalanceLogType type, String remark, String orderNo, String objectId, Channel channel , CancelInsuranceDTO cancelInsuranceDTO) {
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
        try {
            LambdaQueryWrapper<UmsAdmin> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(UmsAdmin::getId, userId);
            queryWrapper.last("FOR UPDATE");
            UmsAdmin user = umsAdminService.getOne(queryWrapper);
            if (user == null) {
                log.error("未找到用户");
                Asserts.fail("未找到用户");
            }

            // 检查保证金
            BigDecimal earnestRate = user.getEarnestRate();
            BigDecimal subEarnestBalance = MathUtil.formatBigDecimal(amount.multiply(earnestRate));

            BigDecimal totalAmount;

            // 检测余额
            BigDecimal balance = user.getBalance();
            if (cancelInsuranceDTO==null){
                totalAmount = amount.add(subEarnestBalance);

            }else {
                  totalAmount = amount.add(subEarnestBalance).add(cancelInsuranceDTO.getInsurance());

            }
            //计算扣费之后的
            BigDecimal afterBalance = balance.subtract(totalAmount);

            // 变更余额
            UpdateWrapper<UmsAdmin> updateWrapper = new UpdateWrapper<>();
            updateWrapper.eq("id", user.getId());

            // updateWrapper.ge("balance", amount);
            updateWrapper.setSql("balance=balance - " + totalAmount);
            //where条件加一句
            updateWrapper.and(wrapper -> wrapper.ge("balance + credit_balance", totalAmount));
            // 增加保证金变动明细
            if (BigDecimal.ZERO.compareTo(subEarnestBalance) < 0) {
                updateWrapper.setSql("earnest_balance=earnest_balance + " + subEarnestBalance);
            }

            // 扣除信用额度
            List<BalanceLog.Detail> details = new ArrayList<>();
            details.add(new BalanceLog.Detail("寄件", amount.toString()));
            details.add(new BalanceLog.Detail("保证金", subEarnestBalance.toString()));
            if (cancelInsuranceDTO!=null){
                try {
                    details.add(new BalanceLog.Detail("保险费", cancelInsuranceDTO.getInsurance().toString()));
                } catch (Exception e) {
                    log.error("加明细"+e.getMessage());
                    e.printStackTrace();
                }

            }


            // 增加余额变动明细
            addLog(userId, totalAmount.multiply(BigDecimal.valueOf(-1)), type, remark, orderNo, afterBalance,
                    BalanceType.BALANCE, user, details);
            boolean update = umsAdminService.update(null, updateWrapper);
            if (!update) {
                log.error("更新用户"+userId+"余额失败-请检查余额是否充足");
                if (channel!=null){
                    try {
                        BaseShipService baseShipService = shipFactory.getBaseShipService(channel);
                        baseShipService.cancelLabel(objectId,orderNo);
                        log.error("更新用户"+userId+"余额失败-请检查余额是否充足-取消三方的下单objectId"+objectId);

                        if (cancelInsuranceDTO!=null){
                            //退保险
                            try {
                                baseShipService.cancelInsurance(cancelInsuranceDTO.getId());
                            } catch (Exception e) {
                                log.error("退保险-"+cancelInsuranceDTO.getId() + e.getMessage());
                                e.printStackTrace();
                            }
                        }
                    } catch (Exception e) {
                         log.error("channelId-"+channel.getId()+"objectId-"+objectId + "用户没钱当场取消下单失败" + e.getMessage());
                    }
                }

                Asserts.fail("更新用户余额失败,请检查余额是否充足");

            }
            // 如果上述操作都成功，那么提交事务
            transactionManager.commit(status);
        } catch (Exception ex) {
            // 如果出现异常，那么回滚事务
            transactionManager.rollback(status);
            throw ex;
        }

        return true;
    }



    @Override
    public CommonResult<String> downloadInvoice(Long rechargeId, HttpServletResponse response) throws IOException {
        UserRecharge userRecharge = this.getOne(new LambdaQueryWrapper<UserRecharge>()
                .eq(UserRecharge::getId, rechargeId)
                .select(UserRecharge::getBalanceType, UserRecharge::getAmount, UserRecharge::getUsdAmount,
                        UserRecharge::getCoinType, UserRecharge::getPaymentType, UserRecharge::getBankName,
                        UserRecharge::getAccountNumber, UserRecharge::getBusinessName, UserRecharge::getConfirmNumber,
                        UserRecharge::getCredentialUrl, UserRecharge::getRemark, UserRecharge::getAuditRemark,
                        UserRecharge::getAuditTime, UserRecharge::getUsdRate));
        /**直接返回流**/
        /*response.reset();
        response.setContentType("application/pdf");
        String filename = System.currentTimeMillis()+".pdf";
        response.addHeader("Content-Disposition", "inline; filename=" + URLUtil.encode(filename, CharsetUtil.CHARSET_UTF_8));*/
        List<AmountDescription> amountDescriptions = Lists.newArrayList();
        amountDescriptions.add(new AmountDescription().setAmount(new BigDecimal("1.0")).setDescription("description1"));
        amountDescriptions.add(new AmountDescription().setAmount(new BigDecimal("1.5")).setDescription("description2"));
        amountDescriptions.add(new AmountDescription().setAmount(new BigDecimal("2.0")).setDescription("description3"));
        amountDescriptions.add(new AmountDescription().setAmount(new BigDecimal("2.5")).setDescription("description4"));
        amountDescriptions.add(new AmountDescription().setAmount(new BigDecimal("3.0")).setDescription("description5"));
        RechargeInvoicePo rechargeInvoicePo = new RechargeInvoicePo().setFileNum("invoice 20240726123422")
                .setCompanyName("测试公司名称").setAddress("测试address").setCity("武汉").setStZipCode("292829832")
                .setPhone("18272637737").setFax("fax").setInvoiceTime("2024-07-29").setBillTo("bill to").setBillFor("bill for")
                .setAddressDetail("CITY, ST ZIP CODE").setPhoneDetail("13233337777").setAmountDetails(amountDescriptions)
                .setSubTotal(new BigDecimal("10")).setTaxRate("0.1%").setOther(BigDecimal.ZERO).setTotal(new BigDecimal("10"));
        VelocityContext context = new VelocityContext();
        // 注入内容
        context.put("fileNum", rechargeInvoicePo.getFileNum());
        context.put("companyName", rechargeInvoicePo.getCompanyName());
        context.put("address", rechargeInvoicePo.getAddress());
        context.put("city", rechargeInvoicePo.getCity());
        context.put("stZipCode", rechargeInvoicePo.getStZipCode());
        context.put("phone", rechargeInvoicePo.getPhone());
        context.put("fax", rechargeInvoicePo.getFax());
        context.put("invoiceTime", rechargeInvoicePo.getInvoiceTime());
        context.put("billTo", rechargeInvoicePo.getBillTo());
        context.put("billFor", rechargeInvoicePo.getBillFor());
        context.put("addressDetail", rechargeInvoicePo.getAddressDetail());
        context.put("phoneDetail", rechargeInvoicePo.getPhoneDetail());
        context.put("amountDetails", rechargeInvoicePo.getAmountDetails());
        context.put("subTotal", rechargeInvoicePo.getSubTotal());
        context.put("taxRate", rechargeInvoicePo.getTaxRate());
        context.put("other", rechargeInvoicePo.getOther());
        context.put("total", rechargeInvoicePo.getTotal());
        // 输出的文件地址
        String tempDir = System.getProperty("java.io.tmpdir");
        // 创建临时文件名
        String fileName = LocalDateTime.now().toString().replace(":", "_") + ".pdf";
        // 拼接临时文件的完整路径
        String localFilePath = tempDir + File.separator + fileName;
        log.info("文件完整路径:{}", localFilePath);
        //final String OUTPUT_FOLDER = "/myfiles//test/outputInvoice.pdf";
        File pdfDest2 = new File(localFilePath );
        FileOutputStream fileOutputStream = new FileOutputStream(pdfDest2);
        // template 模板文件地址
        final String TEMPLATE_PATH = "static/invoiceTemplate.html";
        //保存到服务器端
        PdfUtil.pdfFile(context, TEMPLATE_PATH, fileOutputStream);
        // 以流的形式返回
        /*try(ServletOutputStream outputStream = response.getOutputStream()){
            PdfUtil.pdfFile(context, "static/invoiceTemplete.html", outputStream);
        }catch (Exception e){
            e.printStackTrace();
        }*/
        String s = Global.DOWNLOAD_BY_FILE_NAME_API + fileName;
        return CommonResult.success(s);
    }

    //充值
    @Override
    @Transactional
    public CommonResult<String> rechargeYear(MaterialRechargeParam materialRechargeParam) {
        UmsAdmin userInfo = AdminUserUtil.getUserInfo();
        if (userInfo.getMemberType().equals(MemberType.USER)) {
            TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
            try {
                LambdaQueryWrapper<UmsAdmin> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(UmsAdmin::getId, userInfo.getId());
                UmsAdmin user = umsAdminService.getOne(queryWrapper);
                if (user == null) {
                    log.error("未找到用户");
                    Asserts.fail("未找到用户");
                }

                // 检查保证金
                BigDecimal earnestRate = user.getEarnestRate();
                // 当前需要额外扣减的金额 金额*保证金比例-- 抽成?担保金?
                BigDecimal subEarnestBalance = MathUtil.formatBigDecimal(materialRechargeParam.getAmount().multiply(earnestRate));


                // 检测余额
                BigDecimal balance = user.getBalance();
                // 总的要扣减金额 金额+(抽成?担保金?)
                BigDecimal totalAmount = materialRechargeParam.getAmount().add(subEarnestBalance);
                // 减完后余额 余额扣减(金额+抽成?担保金?)
                BigDecimal afterBalance = balance.subtract(totalAmount);

                if (afterBalance.compareTo(BigDecimal.ZERO) < 0) {
                    Asserts.fail("余额不足,请充值");
                }

                // 变更余额
                UpdateWrapper<UmsAdmin> updateWrapper = new UpdateWrapper<>();
                updateWrapper.eq("id", user.getId());

                // updateWrapper.ge("balance", amount);
                updateWrapper.setSql("balance=balance - " + totalAmount);
                //where条件加一句
                updateWrapper.and(wrapper -> wrapper.ge("balance + credit_balance", totalAmount));
                // 增加保证金变动明细
                if (BigDecimal.ZERO.compareTo(subEarnestBalance) < 0) {

                    updateWrapper.setSql("earnest_balance=earnest_balance + " + subEarnestBalance);
                    //BigDecimal afterEarnestBalance = user.getEarnestBalance().add(subEarnestBalance);
                    // 记录保证金明细，保证可追溯
                    //   addLog(userId, subEarnestBalance, type, remark, orderNo, afterEarnestBalance, BalanceType.EARNEST, user);
                }

                // 扣除信用额度
                List<BalanceLog.Detail> details = new ArrayList<>();
                details.add(new BalanceLog.Detail("物流订阅", materialRechargeParam.getAmount().toString()));
                details.add(new BalanceLog.Detail("保证金", subEarnestBalance.toString()));
                // 增加余额变动明细
                materialRechargeParam.setType(BalanceLogType.SUBSCRIBE).setRemark("物流订阅");
                addLog(userInfo.getId(), totalAmount.multiply(BigDecimal.valueOf(-1)), materialRechargeParam.getType(),
                        materialRechargeParam.getRemark(), materialRechargeParam.getOrderNo(), afterBalance,
                        BalanceType.BALANCE, user, details);
                boolean update = umsAdminService.update(null, updateWrapper);

                // 插入订阅记录
                UmsSubscribeLog umsSubscribeLog = new UmsSubscribeLog();
                umsSubscribeLog.setUserId(userInfo.getId());
                umsSubscribeLog.setRate(String.valueOf(materialRechargeParam.getAmount()));
                umsSubscribeLog.setCreateTime(new Date());
                int insert = subscribeLogMapper.insert(umsSubscribeLog);

                //int i = 1/0;
                if (!update) {
                    log.error("更新用户{}余额失败-请检查余额是否充足", userInfo.getId());

                    // 这里没有选channel 不需要取消
                /*BaseShipService baseShipService = shipFactory.getBaseShipService(channel);
                baseShipService.cancelLabel(objectId);*/
                    Asserts.fail("更新用户余额失败,请检查余额是否充足");

                }
                // 如果上述操作都成功，那么提交事务
                transactionManager.commit(status);
            } catch (Exception ex) {
                // 如果出现异常，那么回滚事务
                transactionManager.rollback(status);
                throw ex;
            }
            return CommonResult.success("订阅成功");
        } else if (userInfo.getMemberType().equals(MemberType.ADMIN)) {
            // 员工可以不用订阅
            return CommonResult.success("员工不用订阅");
        }
        return CommonResult.success("ok");
    }

    @Override
    @Transactional
    public CommonResult<List<FindZoneVo>> getAllZone(FindZoneRequest findZoneRequest) {
        UmsAdmin userInfo = AdminUserUtil.getUserInfo();
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
        final BigDecimal SEARCH_AMOUNT = new BigDecimal("0.1");
        List<FindZoneVo> zones = Lists.newArrayList();
        try {

            //List<String> zone = FedexRateTool.getZone(findZoneRequest.getFrom(), findZoneRequest.getTo(),3);
            List<String> zone = fedexRateTool.getZone(findZoneRequest.getFrom(), findZoneRequest.getTo(),3);
            if (CollUtil.isNotEmpty(zone)) {
                zones = zone.stream().map(item -> {
                    FindZoneVo findZoneVo = new FindZoneVo();
                    findZoneVo.setZone(item);
                    return findZoneVo;
                }).collect(Collectors.toList());
            }

            if (!(userInfo.getMemberType().getValue() == MemberType.ADMIN.getValue())) {

                LambdaQueryWrapper<UmsAdmin> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(UmsAdmin::getId, userInfo.getId());
                //queryWrapper.last("FOR UPDATE"); 取消悲观锁
                UmsAdmin user = umsAdminService.getOne(queryWrapper);
                if (user == null) {
                    log.error("未找到用户");
                    Asserts.fail("未找到用户");
                }

                // 检查保证金
                BigDecimal earnestRate = user.getEarnestRate();
                // 当前需要额外扣减的金额 金额*保证金比例-- 抽成?担保金?
                BigDecimal subEarnestBalance = MathUtil.formatBigDecimal(SEARCH_AMOUNT.multiply(earnestRate));


                // 检测余额
                BigDecimal balance = user.getBalance();
                // 总的要扣减金额 金额+(抽成?担保金?)
                BigDecimal totalAmount = SEARCH_AMOUNT.add(subEarnestBalance);
                // 减完后余额 余额扣减(金额+抽成?担保金?)
                BigDecimal afterBalance = balance.subtract(totalAmount);

                // 变更余额
                UpdateWrapper<UmsAdmin> updateWrapper = new UpdateWrapper<>();
                updateWrapper.eq("id", user.getId());

                // updateWrapper.ge("balance", amount);
                updateWrapper.setSql("balance=balance - " + totalAmount);
                //where条件加一句
                updateWrapper.and(wrapper -> wrapper.ge("balance + credit_balance", totalAmount));
                // 增加保证金变动明细
                if (BigDecimal.ZERO.compareTo(subEarnestBalance) < 0) {

                    updateWrapper.setSql("earnest_balance=earnest_balance + " + subEarnestBalance);
                    //BigDecimal afterEarnestBalance = user.getEarnestBalance().add(subEarnestBalance);
                    // 记录保证金明细，保证可追溯
                    //   addLog(userId, subEarnestBalance, type, remark, orderNo, afterEarnestBalance, BalanceType.EARNEST, user);
                }

                // 扣除信用额度
                List<BalanceLog.Detail> details = new ArrayList<>();
                details.add(new BalanceLog.Detail("findZone", SEARCH_AMOUNT.toString()));
                details.add(new BalanceLog.Detail("保证金", subEarnestBalance.toString()));
                // 增加余额变动明细

                addLog(userInfo.getId(), totalAmount.multiply(BigDecimal.valueOf(-1)), BalanceLogType.FIND_ZONE,
                        "find zone", null, afterBalance,
                        BalanceType.BALANCE, user, details);
                boolean update = umsAdminService.update(null, updateWrapper);

                if (!update) {
                    log.error("更新用户{}余额失败-请检查余额是否充足", userInfo.getId());

                    Asserts.fail("更新用户余额失败,请检查余额是否充足");

                }
                // 如果上述操作都成功，那么提交事务
                transactionManager.commit(status);

            }
        } catch (Exception ex) {
            // 如果出现异常，那么回滚事务
            transactionManager.rollback(status);
            throw ex;
        }

        return CommonResult.success(zones);
    }

    private void addLog(Integer userId, BigDecimal amount, BalanceLogType type, String remark, String orderNo,
                        BigDecimal afterBalance, BalanceType typeBalance, UmsAdmin user, List<BalanceLog.Detail> details) {
        BalanceLog log = new BalanceLog();
        log.setUserId(userId.intValue());
        log.setAfterValue(afterBalance);
        log.setChangeValue(amount);
        log.setRemark(remark);
        log.setType(type);
        log.setBalanceType(typeBalance);
        log.setOrderNo(orderNo);
        log.setSellerId(user.getSellerId());
        log.setDetails(new Gson().toJson(details) );
        if (!balanceLogService.save(log)) {
            Asserts.fail("写入明细失败");
        }
    }


}
